<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    JWT原理详解 |  虎虎生辉
  </title>
  
  <link rel="shortcut icon" href="/blog/favicon.ico" />
  
  
<link rel="stylesheet" href="/blog/css/style.css">

  
<script src="/blog/js/pace.min.js"></script>


  

  

<meta name="generator" content="Hexo 6.0.0"></head>

</html>

<body>
  <div id="app">
    <main class="content">
      <section class="outer">
  <article id="post-JWTa" class="article article-type-post" itemscope
  itemprop="blogPost" data-scroll-reveal>

  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  JWT原理详解
</h1>
  

    </header>
    

    
    <div class="article-meta">
      <a href="/blog/2019/03/01/JWTa/" class="article-date">
  <time datetime="2019-03-01T08:28:11.000Z" itemprop="datePublished">2019-03-01</time>
</a>
      
    </div>
    

    
    
    <div class="tocbot"></div>





    

    <div class="article-entry" itemprop="articleBody">
      


      

      
      <p>随着前后端分离的发展，以及数据中心的建立，越来越多的公司会创建一个中心服务器，服务于各种产品线。</p>
<p>而这些产品线上的产品，它们可能有着各种终端设备，包括但不仅限于浏览器、桌面应用、移动端应用、平板应用、甚至智能家居</p>
<p><img src="http://mdrs.yuanjin.tech/img/image-20200422163727151.png" alt="image-20200422163727151"></p>
<blockquote>
<p>实际上，不同的产品线通常有自己的服务器，产品内部的数据一般和自己的服务器交互。</p>
<p>但中心服务器仍然有必要存在，因为同一家公司的产品总是会存在共享的数据，比如用户数据</p>
</blockquote>
<p>这些设备与中心服务器之间会进行http通信</p>
<p>一般来说，中心服务器至少承担着认证和授权的功能，例如登录：各种设备发送消息到中心服务器，然后中心服务器响应一个身份令牌（参见<a target="_blank" rel="noopener" href="http://www.yuanjin.tech/article/98">cookie原理详解</a>）</p>
<p>当这种结构出现后，就出现一个问题：它们之间还能使用传统的cookie方式传递令牌信息吗？</p>
<p>其实，也是可以的🐶，因为cookie在传输中无非是一个消息头而已，只不过浏览器对这个消息头有特殊处理罢了。</p>
<p>但浏览器之外的设备肯定不喜欢cookie，因为浏览器有着对cookie完善的管理机制，但是在其他设备上，就需要开发者自己手动处理了</p>
<p>jwt的出现就是为了解决这个问题</p>
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>jwt全称<code>Json Web Token</code>，强行翻译过来就是<code>json格式的互联网令牌</code>（算了，还是不要强行翻译了🐷）</p>
<p>它要解决的问题，就是为多种终端设备，提供<strong>统一的、安全的</strong>令牌格式</p>
<p><img src="http://mdrs.yuanjin.tech/img/image-20200422165350268.png" alt="image-20200422165350268"></p>
<p>因此，jwt只是一个令牌格式而已，你可以把它存储到cookie，也可以存储到localstorage，没有任何限制！</p>
<p>同样的，对于传输，你可以使用任何传输方式来传输jwt，一般来说，我们会使用消息头来传输它</p>
<p>比如，当登录成功后，服务器可以给客户端响应一个jwt：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 200 OK</span><br><span class="line">...</span><br><span class="line">set-cookie:token=jwt令牌</span><br><span class="line">authorization:jwt令牌</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">&#123;..., token:jwt令牌&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，jwt令牌可以出现在响应的任何一个地方，客户端和服务器自行约定即可。</p>
<blockquote>
<p>当然，它也可以出现在响应的多个地方，比如为了充分利用浏览器的cookie，同时为了照顾其他设备，也可以让jwt出现在<code>set-cookie</code>和<code>authorization或body</code>中，尽管这会增加额外的传输量。</p>
</blockquote>
<p>当客户端拿到令牌后，它要做的只有一件事：存储它。</p>
<p>你可以存储到任何位置，比如手机文件、PC文件、localstorage、cookie</p>
<p>当后续请求发生时，你只需要将它作为请求的一部分发送到服务器即可。</p>
<p>虽然jwt没有明确要求应该如何附带到请求中，但通常我们会使用如下的格式：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">GET /api/resources HTTP/1.1</span><br><span class="line">...</span><br><span class="line">authorization: bearer jwt令牌</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<blockquote>
<p>这种格式是OAuth2附带token的一种规范格式</p>
<p>至于什么是OAuth2，那是另一个话题了</p>
</blockquote>
<p>这样一来，服务器就能够收到这个令牌了，通过对令牌的验证，即可知道该令牌是否有效。</p>
<p>它们的完整交互流程是非常简单清晰的</p>
<p><img src="http://mdrs.yuanjin.tech/img/image-20200422172837190.png" alt="image-20200422172837190"></p>
<h1 id="令牌的组成"><a href="#令牌的组成" class="headerlink" title="令牌的组成"></a>令牌的组成</h1><p>为了保证令牌的安全性，jwt令牌由三个部分组成，分别是：</p>
<ol>
<li>header：令牌头部，记录了整个令牌的类型和签名算法</li>
<li>payload：令牌负荷，记录了保存的主体信息，比如你要保存的用户信息就可以放到这里</li>
<li>signature：令牌签名，按照头部固定的签名算法对整个令牌进行签名，该签名的作用是：保证令牌不被伪造和篡改</li>
</ol>
<p>它们组合而成的完整格式是：<code>header.payload.signature</code></p>
<p>比如，一个完整的jwt令牌如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc</span><br></pre></td></tr></table></figure>

<p>它各个部分的值分别是：</p>
<ul>
<li><code>header：eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</code></li>
<li><code>payload：eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9</code></li>
<li><code>signature: BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc</code></li>
</ul>
<p>下面分别对每个部分进行说明</p>
<h2 id="header"><a href="#header" class="headerlink" title="header"></a>header</h2><p>它是令牌头部，记录了整个令牌的类型和签名算法</p>
<p>它的格式是一个<code>json</code>对象，如下：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;alg&quot;</span>:<span class="string">&quot;HS256&quot;</span>,</span><br><span class="line">  <span class="attr">&quot;typ&quot;</span>:<span class="string">&quot;JWT&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>该对象记录了：</p>
<ul>
<li>alg：signature部分使用的签名算法，通常可以取两个值<ul>
<li>HS256：一种对称加密算法，使用同一个秘钥对signature加密解密</li>
<li>RS256：一种非对称加密算法，使用私钥加密，公钥解密</li>
</ul>
</li>
<li>typ：整个令牌的类型，固定写<code>JWT</code>即可</li>
</ul>
<p>设置好了<code>header</code>之后，就可以生成<code>header</code>部分了</p>
<p>具体的生成方式及其简单，就是把<code>header</code>部分使用<code>base64 url</code>编码即可</p>
<blockquote>
<p><code>base64 url</code>不是一个加密算法，而是一种编码方式，它是在<code>base64</code>算法的基础上对<code>+</code>、<code>=</code>、<code>/</code>三个字符做出特殊处理的算法</p>
<p>而<code>base64</code>是使用64个可打印字符来表示一个二进制数据，具体的做法参考<a target="_blank" rel="noopener" href="https://baike.baidu.com/item/base64/8545775?fr=aladdin">百度百科</a></p>
</blockquote>
<p>浏览器提供了<code>btoa</code>函数，可以完成这个操作：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.btoa(<span class="built_in">JSON</span>.stringify(&#123;</span><br><span class="line">  <span class="string">&quot;alg&quot;</span>:<span class="string">&quot;HS256&quot;</span>,</span><br><span class="line">  <span class="string">&quot;typ&quot;</span>:<span class="string">&quot;JWT&quot;</span></span><br><span class="line">&#125;))</span><br><span class="line"><span class="comment">// 得到字符串：eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span></span><br></pre></td></tr></table></figure>

<p>同样的，浏览器也提供了<code>atob</code>函数，可以对其进行解码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.atob(<span class="string">&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&quot;</span>)</span><br><span class="line"><span class="comment">// 得到字符串：&#123;&quot;alg&quot;:&quot;HS256&quot;,&quot;typ&quot;:&quot;JWT&quot;&#125;</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>nodejs中没有提供这两个函数，可以安装第三方库<code>atob</code>和<code>bota</code>搞定</p>
<p>或者，手动搞定</p>
</blockquote>
<h2 id="payload"><a href="#payload" class="headerlink" title="payload"></a>payload</h2><p>这部分是jwt的主体信息，它仍然是一个JSON对象，它可以包含以下内容：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;ss&quot;</span>：<span class="attr">&quot;发行者&quot;</span>,</span><br><span class="line">	<span class="attr">&quot;iat&quot;</span>：<span class="attr">&quot;发布时间&quot;</span>,</span><br><span class="line">	<span class="attr">&quot;exp&quot;</span>：<span class="attr">&quot;到期时间&quot;</span>,</span><br><span class="line">	<span class="attr">&quot;sub&quot;</span>：<span class="attr">&quot;主题&quot;</span>,</span><br><span class="line">	<span class="attr">&quot;aud&quot;</span>：<span class="attr">&quot;听众&quot;</span>,</span><br><span class="line">	<span class="attr">&quot;nbf&quot;</span>：<span class="attr">&quot;在此之前不可用&quot;</span>,</span><br><span class="line">  <span class="attr">&quot;jti&quot;</span>：<span class="attr">&quot;JWT ID&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上属性可以全写，也可以一个都不写，它只是一个规范，就算写了，也需要你在将来验证这个jwt令牌时手动处理才能发挥作用</p>
<p>上述属性表达的含义分别是：</p>
<ul>
<li>ss：发行该jwt的是谁，可以写公司名字，也可以写服务名称</li>
<li>iat：该jwt的发放时间，通常写当前时间的时间戳</li>
<li>exp：该jwt的到期时间，通常写时间戳</li>
<li>sub：该jwt是用于干嘛的</li>
<li>aud：该jwt是发放给哪个终端的，可以是终端类型，也可以是用户名称，随意一点</li>
<li>nbf：一个时间点，在该时间点到达之前，这个令牌是不可用的</li>
<li>jti：jwt的唯一编号，设置此项的目的，主要是为了防止重放攻击（重放攻击是在某些场景下，用户使用之前的令牌发送到服务器，被服务器正确的识别，从而导致不可预期的行为发生）</li>
</ul>
<p>可是到现在，看了半天，没有出现我想要写入的数据啊😂</p>
<p>当用户登陆成功之后，我可能需要把用户的一些信息写入到jwt令牌中，比如用户id、账号等等（密码就算了😳）</p>
<p>其实很简单，payload这一部分只是一个json对象而已，你可以向对象中加入任何想要加入的信息</p>
<p>比如，下面的json对象仍然是一个有效的payload</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;foo&quot;</span>:<span class="string">&quot;bar&quot;</span>,</span><br><span class="line">  <span class="attr">&quot;iat&quot;</span>:<span class="number">1587548215</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>foo: bar</code>是我们自定义的信息，<code>iat: 1587548215</code>是jwt规范中的信息</p>
<p>最终，payload部分和header一样，需要通过<code>base64 url</code>编码得到：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.btoa(<span class="built_in">JSON</span>.stringify(&#123;</span><br><span class="line">  <span class="string">&quot;foo&quot;</span>:<span class="string">&quot;bar&quot;</span>,</span><br><span class="line">  <span class="string">&quot;iat&quot;</span>:<span class="number">1587548215</span></span><br><span class="line">&#125;))</span><br><span class="line"><span class="comment">// 得到字符串：eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9</span></span><br></pre></td></tr></table></figure>

<h2 id="signature"><a href="#signature" class="headerlink" title="signature"></a>signature</h2><p>这一部分是jwt的签名，正是它的存在，保证了整个jwt不被篡改</p>
<p>这部分的生成，是对前面两个部分的编码结果，按照头部指定的方式进行加密</p>
<p>比如：头部指定的加密方法是<code>HS256</code>，前面两部分的编码结果是<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9</code></p>
<p>则第三部分就是用对称加密算法<code>HS256</code>对字符串<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9</code>进行加密，当然你得指定一个秘钥，比如<code>shhhhh</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">HS256(<span class="string">`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9`</span>, <span class="string">&quot;shhhhh&quot;</span>)</span><br><span class="line"><span class="comment">// 得到：BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc</span></span><br></pre></td></tr></table></figure>

<p>最终，将三部分组合在一起，就得到了完整的jwt</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc</span><br></pre></td></tr></table></figure>

<p>由于签名使用的秘钥保存在服务器，这样一来，客户端就无法伪造出签名，因为它拿不到秘钥。</p>
<p>换句话说，之所以说无法伪造jwt，就是因为第三部分的存在。</p>
<p>而前面两部分并没有加密，只是一个编码结果而已，可以认为几乎是明文传输</p>
<blockquote>
<p>这不会造成太大的问题，因为既然用户登陆成功了，它当然有权力查看自己的用户信息</p>
<p>甚至在某些网站，用户的基本信息可以被任何人查看</p>
<p>你要保证的，是不要把敏感的信息存放到jwt中，比如密码</p>
</blockquote>
<p>jwt的<code>signature</code>可以保证令牌不被伪造，那如何保证令牌不被篡改呢？</p>
<p>比如，某个用户登陆成功了，获得了jwt，但他人为的篡改了<code>payload</code>，比如把自己的账户余额修改为原来的两倍，然后重新编码出<code>payload</code>发送到服务器，服务器如何得知这些信息被篡改过了呢？</p>
<p>这就要说到令牌的验证了</p>
<h1 id="令牌的验证"><a href="#令牌的验证" class="headerlink" title="令牌的验证"></a>令牌的验证</h1><p><img src="http://mdrs.yuanjin.tech/img/image-20200422172837190.png" alt="image-20200422172837190"></p>
<p>令牌在服务器组装完成后，会以任意的方式发送到客户端</p>
<p>客户端会把令牌保存起来，后续的请求会将令牌发送给服务器</p>
<p>而服务器需要验证令牌是否正确，如何验证呢？</p>
<p>首先，服务器要验证这个令牌是否被篡改过，验证方式非常简单，就是对<code>header+payload</code>用同样的秘钥和加密算法进行重新加密</p>
<p>然后把加密的结果和传入jwt的<code>signature</code>进行对比，如果完全相同，则表示前面两部分没有动过，就是自己颁发的，如果不同，肯定是被篡改过了。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">传入的header.传入的payload.传入的signature</span><br><span class="line">新的signature = header中的加密算法(传入的header.传入的payload, 秘钥)</span><br><span class="line">验证：新的signature == 传入的signature</span><br></pre></td></tr></table></figure>

<p>当令牌验证为没有被篡改后，服务器可以进行其他验证：比如是否过期、听众是否满足要求等等，这些就视情况而定了</p>
<p>注意：这些验证都需要服务器手动完成，没有哪个服务器会给你进行自动验证，当然，你可以借助第三方库来完成这些操作</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>最后，总结一下jwt的特点：</p>
<ul>
<li>jwt本质上是一种令牌格式。它和终端设备无关，同样和服务器无关，甚至与如何传输无关，它只是规范了令牌的格式而已</li>
<li>jwt由三部分组成：header、payload、signature。主体信息在payload</li>
<li>jwt难以被篡改和伪造。这是因为有第三部分的签名存在。</li>
</ul>

      
      <!-- 打赏 -->
      
        <div id="reward-btn">
          打赏
        </div>
        
    </div>
    <footer class="article-footer">
      <a data-url="https://gitee.com/huafu123/blog/2019/03/01/JWTa/" data-id="ckya9fc12000hygf60lmr8s3p"
        class="article-share-link">分享</a>
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/blog/tags/jwt/" rel="tag">jwt</a></li></ul>

    </footer>

  </div>

  
  
  <nav class="article-nav">
    
      <a href="/blog/2019/04/01/Cookiea/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            cookie的基本概念
          
        </div>
      </a>
    
    
      <a href="/blog/2019/02/01/NODEJSa/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">NodeJs 初识</div>
      </a>
    
  </nav>


  

  
  
<!-- valine评论 -->
<div id="vcomments-box">
    <div id="vcomments">
    </div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='https://cdn.jsdelivr.net/npm/valine@1.3.10/dist/Valine.min.js'></script>
<script>
    new Valine({
        el: '#vcomments',
        notify: false,
        verify: false,
        app_id: '',
        app_key: '',
        path: window.location.pathname,
        avatar: 'mp',
        placeholder: '给我的文章加点评论吧~',
        recordIP: true
    });
    const infoEle = document.querySelector('#vcomments .info');
    if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
        infoEle.childNodes.forEach(function (item) {
            item.parentNode.removeChild(item);
        });
    }
</script>
<style>
    #vcomments-box {
        padding: 5px 30px;
    }

    @media screen and (max-width: 800px) {
        #vcomments-box {
            padding: 5px 0px;
        }
    }

    #vcomments-box #vcomments {
        background-color: #fff;
    }

    .v .vlist .vcard .vh {
        padding-right: 20px;
    }

    .v .vlist .vcard {
        padding-left: 10px;
    }
</style>

  

  
  
  

</article>
</section>
      <footer class="footer">
  <div class="outer">
    <ul class="list-inline">
      <li>
        &copy;
        2020-2022
        Huafu Li
      </li>
      <li>
        
          Powered by
        
        
        <a href="https://hexo.io" target="_blank">Hexo</a> Theme <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul class="list-inline">
      <li>
        
        
        <ul class="list-inline">
  <li>PV:<span id="busuanzi_value_page_pv"></span></li>
  <li>UV:<span id="busuanzi_value_site_uv"></span></li>
</ul>
        
      </li>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
    <div class="to_top">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>
      </div>
    </main>
    
    <aside class="sidebar">
      
        <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/blog/"><img src="/blog/hu.png" alt="虎虎生辉"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/blog/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/blog/archives">目录</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/blog/about">关于</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/blog/pictures">相册</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/blog/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
      </aside>
      <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/blog/./images/alipay.png">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/blog/./images/wechat.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
      
<script src="/blog/js/jquery-2.0.3.min.js"></script>


<script src="/blog/js/jquery.justifiedGallery.min.js"></script>


<script src="/blog/js/lazyload.min.js"></script>


<script src="/blog/js/busuanzi-2.3.pure.min.js"></script>


  
<script src="/blog/fancybox/jquery.fancybox.min.js"></script>




  
<script src="/blog/js/tocbot.min.js"></script>

  <script>
    // Tocbot_v4.7.0  http://tscanlin.github.io/tocbot/
    tocbot.init({
      tocSelector: '.tocbot',
      contentSelector: '.article-entry',
      headingSelector: 'h1, h2, h3, h4, h5, h6',
      hasInnerContainers: true,
      scrollSmooth: true,
      positionFixedSelector: '.tocbot',
      positionFixedClass: 'is-position-fixed',
      fixedSidebarOffset: 'auto',
			onClick: (e) => {
      	document.getElementById(e.target.innerText).scrollIntoView()
      	return false;
    	}
    });
  </script>


<script>
  var ayerConfig = {
    mathjax: false
  }
</script>


<script src="/blog/js/ayer.js"></script>


<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">



<script type="text/javascript" src="https://js.users.51.la/20544303.js"></script>
  
  

  </div>
</body>

</html>